其他错误处理策略

  原来的错误处理代码比采用异常之后的代码更短,也更优美。但那却是通过程序各部分之间的紧密耦合而得到的。那种方式不能很好地用到由分别开发的库组合而成的程序中。

  我们也可以考虑去掉专门的错误处理函数skip(),方式是在main()里引进一个状态变量。例如,

    int main(int argc, char* argv[])        // 糟糕风格的例子
    {
        // ...
        bool in_error = false;
        while(*Driver::input) {
            try {
                Lexer::get_token();
                if(Lexer::curr_tok == Lexer::END) break;
                if(Lexer::curr_tok == Lexer::PRINT) {
                    in_error = false;
                    continue;
                }
                if(in_error == false) cout << Parser::expr(false) << '\n';
            }
            catch (Error::Zero_divide) {
                cerr << "attempt to divide by zero\n";
                in_error = true;
            }
            catch (Error::Syntax_error e) {
                cerr << "syntax error: " << e.p << '\n';
                in_error = true;
            }
        }
        if(Driver::input != &std::cin) delete Driver::input;
        return Driver::no_of_errors;
    }

我认为这是一个很坏的办法,因为:

  1)、状态变量是混乱和错误的一个常见根源,特别是允许它们大量出现并影响程序中较大的片段时。特别地,我认为使用in_error的main()版本不如使用skip()的版本好读。

  2)、一般说,使处理错误的代码与“正常”代码分离是一种很好的策略。

  3)、在导致错误的代码的同一个抽象层次上处理错误是非常危险的。完成错误处理的代码有可能又产生了引起错误处理的那个错误。我把下面问题留做练习:请说明在使用in_error的main()版本中呢怎么会出现这种情况(8.5[7])。

  4)、修改“正常”的代码,加上错误处理代码,所需的工作量比增加单独的错误处理例行程序更多。

异常处理的意图是去处理非局部的问题。如果一个错误可以在局部处理,那么(几乎)总应该这样做。例如,完全没有必要对“过多参数错误”使用异常机制:

    int main(int argc, char* argv[])
    {
        using namespace std;
        using namespace Driver;

        switch (argc) {
        case 1:
            input = &cin;
            break;
        case 2:
            input = new istringstream(argv[1]);
            break;
        default:
            cerr << "too many arguments\n";
            return 1;
        }

        // 如前
    }

第14章将进一步讨论有关异常的问题。

🔚